using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;

namespace SmartLauncher
{
	/// <summary>
	/// This clas is responsible for updating the application and all documents
	/// located in the application directory. It does not update subdirectories.
	/// </summary>
	public static class SelfUpdateEngine
	{
		private const string PFX_OLD = "old_";
		public const string PFX_UPDATE = "Self";

		private static TimeSpan remoteTime;
		private static TimeSpan localTime;

		public static TimeSpan LocalTime
		{
			get { return localTime; }
		}

		public static TimeSpan RemoteTime
		{
			get { return remoteTime; }
		}

		// === 
		/// <summary>
		/// I consider the connectivity is low when the time needed to collect information
		/// about the remote application is higher than collecting data on the local 
		/// application + 
		/// 
		/// collecting information of the
		/// remote application is 
		/// </summary>
		public static bool LowConnectivity
		{
			get { return (remoteTime > localTime); }
		}

		public static bool HighConnectivity
		{
			get { return !LowConnectivity; }
		}

		public static void Update(string remoteFolder)
		{
			string localFolder;
			string currentApplication;
			string updateApplication;
			string updateConfiguration;
			string remoteApplication;
			string remoteConfiguration;
			RepositoryItem remoteItem;
			RepositoryItem localItem;
			TimeSpan remoteStart;
			TimeSpan remoteStop;
			TimeSpan localStart;
			TimeSpan localStop;

			// === If Self-Update is not active, go out ============================
			// === No update when starting in automated mode =======================
			if ((Properties.Settings.Default.SelfUpdateEnabled == false) ||
				(ApplicationStartInfo.StartMode == StartModes.Automated))
				return;

			localFolder = ApplicationStartInfo.LocalRoot;

			// === Check if application asked to update itselfs ====================
			if (ApplicationStartInfo.StartMode == StartModes.SelfUpdate)
			{
				// === Enter in self-update mode =====================================
				// --- Remove old files, if exist from a previous tentative ----------
				RemoveOldFiles(localFolder);

				// --- Backup existing files -----------------------------------------
				BackupOldFiles(localFolder);

				// --- Copy new files ------------------------------------------------
				RetrieveNewFiles(localFolder, remoteFolder);

				// --- Remove old files, from this tentative -------------------------
				RemoveOldFiles(localFolder);

				// --- Start new version of the application --------------------------
				currentApplication = Environment.GetCommandLineArgs()[0].ToLower().Replace(".vshost", "").Replace(string.Format("{0}_", PFX_UPDATE.ToLower()), "");
				Process.Start(currentApplication);
				Environment.Exit(0);
			}
			else
				if (ApplicationStartInfo.StartMode == StartModes.Interactive)
				{
					// === Verify possible new version of the program ==================
					// --- Remove old files, if exist from a previous tentative ----------
					RemoveOldFiles(localFolder);

					// === Copy the new version of the application =====================
					// Only the application is copied: if the Self-Update has been updated
					// the self-update process will run in its new version
					currentApplication = Environment.GetCommandLineArgs()[0].ToLower().Replace(".vshost", "");
					updateApplication = Path.Combine(Path.GetDirectoryName(currentApplication), string.Format("{0}_{1}", PFX_UPDATE, Path.GetFileName(currentApplication)));
					updateConfiguration = updateApplication + ".config";

					// --- Delete the old Self_Application.exe -------------------------
					if (File.Exists(updateApplication) == true)
						try
						{
							File.Delete(updateApplication);
						}
						catch { }

					if (File.Exists(updateConfiguration) == true)
						try
						{
							File.Delete(updateConfiguration);
						}
						catch { }

					// --- Check for new version ---------------------------------------
					remoteApplication = Path.Combine(remoteFolder, Path.GetFileName(currentApplication));
					remoteConfiguration = remoteApplication + ".config";

					if (File.Exists(remoteApplication) == false) return;
					remoteStart = DateTime.Now.TimeOfDay;
					remoteItem = new RepositoryItem(remoteApplication, false);
					remoteStop = DateTime.Now.TimeOfDay;
					remoteTime = remoteStop - remoteStart;

					localStart = DateTime.Now.TimeOfDay;
					localItem = new RepositoryItem(currentApplication, true);
					localStop = DateTime.Now.TimeOfDay;
					localTime = localStop - localStart;

					// --- Are the versions different? ---------------------------------
					if ((localItem.VersionDate != remoteItem.VersionDate) &&
						((Properties.Settings.Default.SelfUpdateSilent == true) ||
						(System.Windows.Forms.MessageBox.Show(string.Format("The official version of [{0}] differs from your local copy. Would you like to update it now?\n\n- Your local has been published on {1}\n- The remote version has been published on {2}", Properties.Settings.Default.ShellHome, localItem.VersionDate, remoteItem.VersionDate), Properties.Settings.Default.ShellHome, System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)))
					{
						// === Copy the new executable =======================================
						File.Copy(remoteApplication, updateApplication, true);
						File.Copy(remoteConfiguration, updateConfiguration, true);
						Process.Start(updateApplication, string.Format("/{0}", PFX_UPDATE));
						Environment.Exit(0);
					}

				}
		}

		private static void RemoveOldFiles(string rootFolder)
		{
			string[] items;

			items = Directory.GetFiles(rootFolder);
			foreach (string item in items)
				if (Path.GetFileName(item).ToLower().StartsWith(PFX_OLD.ToLower()) == true)
					try
					{
						File.Delete(item);
					}
					catch
					{
						// --- The file cannot be deleted, skip it -----------------------
						if (Properties.Settings.Default.SelfUpdateSilent == false)
							System.Windows.Forms.MessageBox.Show(
								string.Format("The file '{0}' could not be removed.", item),
								Properties.Settings.Default.ShellHome,
								System.Windows.Forms.MessageBoxButtons.OK,
								System.Windows.Forms.MessageBoxIcon.Warning);
					}
		}

		private static void BackupOldFiles(string rootFolder)
		{
			string[] items;
			string oldFilename;

			items = Directory.GetFiles(rootFolder);
			foreach (string item in items)
				if (Path.GetFileName(item).ToLower().StartsWith(PFX_UPDATE.ToLower()) == false)
				{
					oldFilename = Path.Combine(Path.GetDirectoryName(item), string.Format("{0}{1}", PFX_OLD, Path.GetFileName(item)));
					if (File.Exists(oldFilename) == true) File.Delete(oldFilename);
					File.Move(item, oldFilename);
				}
		}

		private static void RetrieveNewFiles(string localFolder, string remoteFolder)
		{
			string[] items;
			string localFilename;

			items = Directory.GetFiles(remoteFolder);
			foreach (string item in items)
			{
				localFilename = Path.Combine(localFolder, Path.GetFileName(item));
				try
				{
					File.Copy(item, localFilename, true);
				}
				catch { }
			}
		}
	}
}